/*
 * Wazuh Vulnerability scanner
 * Copyright (C) 2015, Wazuh Inc.
 * March 25, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef _DATABASE_FEED_MANAGER_HPP
#define _DATABASE_FEED_MANAGER_HPP

#include "../policyManager/policyManager.hpp"
#include "cacheLRU.hpp"
#include "conditionSync.hpp"
#include "contentRegister.hpp"
#include "eventDecoder.hpp"
#include "globalData.hpp"
#include "jsonArrayParser.hpp"
#include "loggerHelper.h"
#include "observer.hpp"
#include "rocksDBWrapper.hpp"
#include "storeModel.hpp"
#include "vulnerabilityCandidate_generated.h"
#include "vulnerabilityDescription_generated.h"
#include "vulnerabilityRemediations_generated.h"
#include "vulnerabilityScanner.hpp"
#include <external/nlohmann/json.hpp>
#include <fstream>
#include <functional>
#include <memory>
#include <string>
#include <vector>

using namespace NSVulnerabilityScanner;
constexpr auto DATABASE_PATH {"queue/vd/feed"};
constexpr auto OFFSET_TRANSACTION_SIZE {1000};
constexpr auto EMPTY_KEY {""};

constexpr auto DEFAULT_ADP {"nvd"};
constexpr auto ADP_CVSS_KEY {"cvss"};
constexpr auto ADP_DESCRIPTION_KEY {"description"};
constexpr auto ADP_REFERENCE_KEY {"reference"};
constexpr auto ADP_CWE_ID_KEY {"cwe"};
constexpr auto ADP_DESCRIPTIONS_MAP_KEY {"adp_descriptions"};
constexpr auto ADP_DEFAULT_ARRAY_KEY {"adp_default_array"};

/**
 * @brief Enum to access the fields of the FileProcessingResult tuple.
 *
 */
enum FileProcessingResultFields
{
    OFFSET = 0,
    HASH,
    STATUS
};

/**
 * @brief Scanning package data struct.
 */
struct PackageData final
{
    std::string name;    ///< Package name.
    std::string vendor;  ///< Package vendor.
    std::string format;  ///< Package format.
    std::string version; ///< Package version.
};

/**
 * @brief A struct for storing a pair of FlatBuffers data.
 *
 * The `FlatbufferDataPair` struct is designed to store a pair of related FlatBuffers data:
 * a `rocksdb::PinnableSlice` containing the serialized data and a pointer to the deserialized
 * data of type `FlatbufferType`. This allows for efficient storage and access to both the raw
 * serialized data and its parsed form.
 *
 * @tparam FlatbufferType The type of the FlatBuffers object that this struct represents.
 */
template<typename FlatbufferType>
struct FlatbufferDataPair final
{
    /**
     * @brief A slice to the serialized FlatBuffers data.
     *
     * The `slice` member stores a `rocksdb::PinnableSlice` that contains the serialized
     * FlatBuffers data.
     */
    rocksdb::PinnableSlice slice;

    /**
     * @brief A pointer to the deserialized FlatBuffers data.
     *
     * The `data` member is a pointer to the deserialized FlatBuffers data of type `FlatbufferType`.
     * It provides direct access to the parsed information.
     */
    const FlatbufferType* data = nullptr;
};

/**
 * @brief TranslatedData structure.
 *
 * This structure holds translated data, including the translated product and vendor information.
 */
struct TranslatedData final
{
    std::string translatedProduct; ///< Translated product information.
    std::string translatedVendor;  ///< Translated vendor information.
    std::string translatedVersion; ///< Translated version information.
};

/**
 * @brief Represents a translation entry containing regular expressions for product and vendor identification,
 *        along with a vector of translated data.
 */
struct Translation final
{
    std::optional<std::regex> productRegex;  ///< Regular expression for product identification.
    std::optional<std::regex> vendorRegex;   ///< Regular expression for vendor identification.
    std::optional<std::regex> versionRegex;  ///< Regular expression for version identification.
    std::vector<TranslatedData> translation; ///< Vector of translated data.
    std::vector<std::string> target;         ///< Vector of valid targets.
};

/**
 * @brief Translations cache.
 * @details Key: Translation ID, Value: Translation information.
 */
using TranslationLRUCache = LRUCache<std::string, Translation>;

/**
 * @brief DatabaseFeedManager class.
 *
 * @tparam TPolicyManager Policy manager type.
 * @tparam TContentRegister Content register type.
 * @tparam TRocksDBWrapper RocksDB wrapper type.
 */
template<typename TPolicyManager = PolicyManager,
         typename TContentRegister = ContentRegister,
         typename TRocksDBWrapper = Utils::RocksDBWrapper>
class TDatabaseFeedManager final : public Observer<nlohmann::json&>
{
public:
    /**
     * @brief Process and validate the message received from the file processing callback.
     *
     * @param message Message received.
     * @param shouldStop Stop condition.
     * @param feedDatabase RocksDB database.
     *
     * @return FileProcessingResult
     */
    FileProcessingResult processMessage(const std::string& message,
                                        std::shared_ptr<ConditionSync> shouldStop,
                                        std::shared_ptr<TRocksDBWrapper> feedDatabase)
    {
        auto parsedMessage = nlohmann::json::parse(message, nullptr, false);

        if (parsedMessage.is_discarded() || !parsedMessage.contains("paths") || !parsedMessage.contains("type") ||
            !parsedMessage.contains("offset"))
        {
            throw std::runtime_error("Invalid message. Missing required fields.");
        }

        auto resourceProcessing = [&message](const nlohmann::json& resource, std::shared_ptr<TRocksDBWrapper> database)
        {
            auto eventContext = std::make_shared<EventContext>(EventContext {.message = message,
                                                                             .resource = resource,
                                                                             .cve5Buffer = {},
                                                                             .feedDatabase = database,
                                                                             .resourceType = ResourceType::UNKNOWN});

            const auto eventDecoder = std::make_shared<EventDecoder>();
            eventDecoder->setLast(std::make_shared<StoreModel>());
            eventDecoder->handleRequest(std::move(eventContext));
        };

        // Lock the mutex to protect the access to the internal databases.
        std::scoped_lock<std::shared_mutex> lock(m_mutex);

        if (parsedMessage.at("type") == "offsets")
        {
            auto currentOffset = 0LL;
            auto jsonPointer {"/data"_json_pointer};
            for (const auto& path : parsedMessage.at("paths"))
            {
                logDebug2(WM_VULNSCAN_LOGTAG, "Processing file: %s", path.get_ref<const std::string&>().c_str());

                // Parse the file and execute the chain/orchestration for each valid resource.
                // The lambda function returns false if the module is stopped.
                // This uses the json sax api, so it is faster than the json tree api and it consumes less memory.
                // LCOV_EXCL_START
                JsonArray::parse(
                    path,
                    [&](nlohmann::json&& item, const size_t)
                    {
                        resourceProcessing(item, feedDatabase);

                        // Extract the offset from the last element.
                        currentOffset = item.at("offset");

                        // Commit the transaction for every 200 elements.
                        return !shouldStop->check();
                    },
                    jsonPointer);
                // LCOV_EXCL_STOP

                if (shouldStop->check())
                {
                    break;
                }
            }

            // Return the current offset and an empty hash.
            logDebug2(WM_VULNSCAN_LOGTAG, "Last offset processed: %lld", currentOffset);
            return {currentOffset, "", true};
        }
        else if (parsedMessage.at("type") == "raw")
        {
            // The message contains a list of files to process.
            // For the case of the raw message, the list of files is always one, because it uses the consolidated file.
            if (parsedMessage.at("paths").size() != 1)
            {
                throw std::runtime_error("Invalid raw message. Multiple files provided.");
            }

            // Delete all the data in the database, because the raw message contains all and latest data.
            feedDatabase->deleteAll();

            int latestOffset = 0;
            for (const auto& path : parsedMessage.at("paths"))
            {
                logDebug2(WM_VULNSCAN_LOGTAG, "Processing file: %s", path.get_ref<const std::string&>().c_str());
                std::ifstream file(path.get_ref<const std::string&>());
                if (!file.is_open())
                {
                    throw std::runtime_error("Unable to open input file: " + path.get_ref<const std::string&>());
                }

                std::string line;
                int32_t step = 0;
                // Parse the file and execute the chain/orchestration for each valid resource.
                // It orchestrate line by line.
                while (std::getline(file, line))
                {
                    if (shouldStop->check())
                    {
                        // We set the offset to 0 to indicate that the process was interrupted but successfully.
                        return {0, "", true};
                    }

                    if (step++ % 1000 == 0)
                    {
                        logDebug2(WM_VULNSCAN_LOGTAG, "Processing line: %d", step);
                    }

                    nlohmann::json parsedLine = nlohmann::json::parse(line, nullptr, false);
                    if (parsedLine.is_discarded() || !parsedLine.contains("name") || !parsedLine.contains("offset"))
                    {
                        throw std::runtime_error("Invalid line found in file: " + path.get_ref<const std::string&>());
                    }

                    // Offsets are not always in order, so we need to get the highest offset.
                    latestOffset = std::max(latestOffset, parsedLine.at("offset").get<int>());

                    parsedLine["resource"] = parsedLine.at("name");
                    parsedLine["type"] = "create";

                    resourceProcessing(parsedLine, feedDatabase);
                }
            }

            // Only offline downloads set the hash in the message. Extract it safely.
            const auto getHash = [&parsedMessage]() -> std::string
            {
                if (parsedMessage.contains("fileMetadata") && parsedMessage.at("fileMetadata").contains("hash"))
                {
                    return std::string(parsedMessage.at("fileMetadata").at("hash"));
                }
                return "";
            };

            return {latestOffset, getHash(), true};
        }
        else
        {
            throw std::runtime_error("Invalid message. Unknown type.");
        }
    }

    /**
     * @brief Class constructor.
     *
     * @param mutex Mutex to protect the access to the internal databases.
     * @param reloadGlobalMapsStartup If true, the vendor and os cpe maps will be reloaded at startup.
     * @param initContentUpdater If true, the content updater will be initialized.
     * @param postUpdateCallback Callback to be executed after the update process (if not provided, uses default
     * behaviour).
     * @param fileProcessingCallback Callback to process the file (if not provided, uses default behaviour).
     */
    // LCOV_EXCL_START
    explicit TDatabaseFeedManager(
        std::shared_mutex& mutex,
        const bool reloadGlobalMapsStartup = true,
        const bool initContentUpdater = true,
        const std::function<void()>& postUpdateCallback =
            []() { // Not used
            },
        FileProcessingCallback fileProcessingCallback = nullptr)
        : Observer("database_feed_manager")
        , m_mutex(mutex)
    {
        const auto updaterPolicy = TPolicyManager::instance().getUpdaterConfiguration();
        const std::string topicName = updaterPolicy.at("topicName");

        try
        {
            // We disable the automatic repair of the DB in case it's corrupted.
            m_feedDatabase = std::make_shared<TRocksDBWrapper>(DATABASE_PATH, false, false);

            // This initializes the vendor and os cpe maps and should be called before any scan or message processing.
            if (reloadGlobalMapsStartup)
            {
                // Try to load global maps from the database, if it fails we throw an exception to force the download of
                // the complete feed.
                reloadGlobalMaps();
            }
        }
        catch (const std::exception& ex)
        {
            // Create the database if it doesn't exist. We must remove any existing directory, as it may be corrupted.
            if (!m_feedDatabase)
            {
                std::filesystem::remove_all(DATABASE_PATH);
                m_feedDatabase = std::make_shared<TRocksDBWrapper>(DATABASE_PATH, false);
            }

            // Remove the updater directory to force the download of the complete feed.
            std::filesystem::remove_all(UPDATER_PATH);

            logError(WM_VULNSCAN_LOGTAG, "Error opening the database: %s.", ex.what());
        }

        if (!fileProcessingCallback)
        {

            fileProcessingCallback = [this, postUpdateCallback, feedDatabase = m_feedDatabase](
                                         const std::string& message, std::shared_ptr<ConditionSync> shouldStop)
            {
                // Initialize the processing result with default values.
                FileProcessingResult processMessageResult {0, "", false};

                try
                {
                    logInfo(WM_VULNSCAN_LOGTAG, "Initiating update feed process.");
                    processMessageResult = processMessage(message, shouldStop, feedDatabase);

                    // If the process was interrupted or it failed, we return prematurely.
                    if (shouldStop->check() || !std::get<FileProcessingResultFields::STATUS>(processMessageResult))
                    {
                        logDebug2(WM_VULNSCAN_LOGTAG,
                                  "Feed update process interrupted or failed. Offset: %d, Hash: %s.",
                                  std::get<FileProcessingResultFields::OFFSET>(processMessageResult),
                                  std::get<FileProcessingResultFields::HASH>(processMessageResult).c_str());
                        return processMessageResult;
                    }

                    // Verify that the global maps values and update the maps in memory
                    this->reloadGlobalMaps();

                    // Dispatch the post update Callback
                    postUpdateCallback();
                    logInfo(WM_VULNSCAN_LOGTAG, "Feed update process completed.");
                }
                catch (const std::exception& e)
                {
                    logError(WM_VULNSCAN_LOGTAG, "Error updating feed: %s.", e.what());
                    processMessageResult = {0, "", false};
                }

                return processMessageResult;
            };
        }

        if (initContentUpdater)
        {
            // Vulnerability content updater initialization.
            m_contentRegistration = std::make_unique<TContentRegister>(
                topicName, TPolicyManager::instance().getUpdaterConfiguration(), fileProcessingCallback);
        }
    }

    /**
     * @brief Retrieves vulnerability remediation information from the database, for a given CVE ID.
     *
     * This function retrieves remediation information associated with a given CVE ID
     * from the underlying database and stores it in the provided `dtoVulnRemediation`
     * object.
     *
     * @param cveId The CVE ID for which remediation information is requested.
     * @param dtoVulnRemediation A reference to a `FlatbufferDataPair` object
     *        where the retrieved remediation information will be stored.
     *
     * @throws std::runtime_error if the retrieved data from the database is invalid or
     *         not in the expected FlatBuffers format.
     */
    void getVulnerabilityRemediation(const std::string& cveId, FlatbufferDataPair<RemediationInfo>& dtoVulnRemediation)
    {
        // If the remediation information is not found in the database, we return because there is no remediation.
        if (auto result = m_feedDatabase->get(cveId, dtoVulnRemediation.slice, REMEDIATIONS_COLUMN); !result)
        {
            return;
        }

        if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(dtoVulnRemediation.slice.data()),
                                           dtoVulnRemediation.slice.size());
            !VerifyRemediationInfoBuffer(verifier))
        {
            throw std::runtime_error("Error: Invalid FlatBuffers data in RocksDB.");
        }

        dtoVulnRemediation.data = GetRemediationInfo(reinterpret_cast<const uint8_t*>(dtoVulnRemediation.slice.data()));
    }

    /**
     * @brief Retrieves the vulnerabilities information from the database, for a given hotfix ID.
     *
     * This function retrieves remediation information associated with a given hotfix from the underlying database and
     * stores it in the 'remediationInfo' object.
     *
     * @param hotfix hotfix id for which remediation information is requested.
     *
     * @return An unordered set containing the CVEs associated with the provided hotfix.
     *
     * @throws std::runtime_error if the retrieved data from the database is invalid or not in the expected FlatBuffers
     * format.
     */
    std::unordered_set<std::string> getHotfixVulnerabilities(const std::string& hotfix)
    {
        std::unordered_set<std::string> hotfixVulnerabilities;
        if (m_feedDatabase->columnExists(HOTFIXES_APPLICATIONS_COLUMN))
        {
            for (const auto& [key, value] : m_feedDatabase->seek(hotfix, HOTFIXES_APPLICATIONS_COLUMN))
            {
                hotfixVulnerabilities.insert(key);
            }
        }
        return hotfixVulnerabilities;
    }

    /**
     * @brief Fills the Level 2 cache with translations from the feed database.
     *
     * This function iterates over translations in the feed database, verifies the integrity of FlatBuffers
     * translation data, and inserts valid translations into the Level 2 cache.
     *
     * @throws std::runtime_error If invalid FlatBuffers translation data is encountered in the database.
     */
    void fillL2CacheTranslations()
    {
        // Iterate over translations in the feed database
        for (const auto& [key, value] : m_feedDatabase->begin(TRANSLATIONS_COLUMN))
        {
            // Check if the cache is full
            if (m_translationL2Cache->isFull())
            {
                break; // Exit the loop if cache is full
            }

            // Verify the integrity of FlatBuffers translation data
            if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(value.data()), value.size());
                !VerifyTranslationEntryBuffer(verifier))
            {
                throw std::runtime_error("Error: Invalid FlatBuffers translation data in RocksDB.");
            }

            // Parse translation data
            auto queryData = GetTranslationEntry(reinterpret_cast<const uint8_t*>(value.data()));

            // Prepare regular expressions for product, vendor and version matching
            auto createRegex = [](const auto regex) -> std::optional<std::regex>
            {
                return regex && !regex->str().empty() ? std::optional<std::regex>(regex->str()) : std::nullopt;
            };

            // Initialize Translation object to store translation data
            Translation translationQuery = {.productRegex = createRegex(queryData->source()->product()),
                                            .vendorRegex = createRegex(queryData->source()->vendor()),
                                            .versionRegex = createRegex(queryData->source()->version()),
                                            .translation = {},
                                            .target = {}};

            // Load target platforms into the Translation object
            for (const auto& target : *queryData->target())
            {
                translationQuery.target.push_back(target->str());
            }

            // Load translation data into the Translation object
            for (const auto& translationData : *queryData->translation())
            {
                translationQuery.translation.emplace_back(TranslatedData {
                    .translatedProduct = translationData->product() ? translationData->product()->str() : "",
                    .translatedVendor = translationData->vendor() ? translationData->vendor()->str() : "",
                    .translatedVersion = translationData->version() ? translationData->version()->str() : ""});
            }

            // Insert translation into cache
            m_translationL2Cache->insertKey(key, translationQuery);
        }
    }

    /**
     * @brief Retrieves translations from the Level 2 cache based on the provided package data and operating system
     * platform.
     *
     * This function searches the Level 2 cache for translations that match the provided package name, vendor and
     * operating system platform. If a translation matches the regex expressions, it is appended to the result vector.
     *
     * @param package A structure containing all the data for the package.
     * @param osPlatform The operating system platform for which translations are requested.
     * @return A vector containing the matching translations for the specified package and platform.
     */
    std::vector<TranslatedData> getTranslationFromL2(const PackageData& package, const std::string& osPlatform)
    {
        // Vector to store the resulting translations
        std::vector<TranslatedData> translationResult;

        // Iterate over the Level 2 cache data
        m_translationL2Cache->forEach(
            [&]([[maybe_unused]] const auto& key, const auto& cacheData)
            {
                /* Check conditions, return true to continue the loop, false to break it */
                // - The target platform matches the provided OS platform
                if (std::find(cacheData.target.begin(), cacheData.target.end(), osPlatform) == cacheData.target.end())
                {
                    return true;
                }
                // - The package name matches the product regex if present
                if (cacheData.productRegex.has_value() &&
                    !std::regex_search(package.name, cacheData.productRegex.value()))
                {
                    return true;
                }
                // - The vendor matches the vendor regex if present
                if (cacheData.vendorRegex.has_value() &&
                    !std::regex_search(package.vendor, cacheData.vendorRegex.value()))
                {
                    return true;
                }

                // Append the matching translation to the result vector
                for (const auto& translatedPackage : cacheData.translation)
                {
                    TranslatedData translatedResult {.translatedProduct = translatedPackage.translatedProduct,
                                                     .translatedVendor = translatedPackage.translatedVendor,
                                                     .translatedVersion = {}};
                    // Search for version regex or use translated version
                    std::smatch stringFound;
                    if (cacheData.versionRegex.has_value() &&
                        std::regex_search(package.name, stringFound, cacheData.versionRegex.value()) &&
                        stringFound.size() > 0)
                    {
                        // We only consider the first capture group
                        translatedResult.translatedVersion = stringFound.str(1);
                    }
                    else
                    {
                        translatedResult.translatedVersion = translatedPackage.translatedVersion;
                    }
                    translationResult.push_back(std::move(translatedResult));
                }

                // Break the loop after finding the first matching translation
                return false;
            });

        // Return the vector containing the matching translations
        return translationResult;
    }

    /**
     * @brief Get the Vulnerabilities Candidates information.
     *
     * @param cnaName RocksDB table identifier.
     * @param package Struct with package data.
     * @param callback Store vulnerability data.
     */
    void getVulnerabilitiesCandidates(
        const std::string& cnaName,
        const PackageData& package,
        const std::function<bool(const std::string& cnaName,
                                 const PackageData& package,
                                 const NSVulnerabilityScanner::ScanVulnerabilityCandidate&)>& callback)
    {
        if (package.name.empty() || cnaName.empty())
        {
            throw std::runtime_error("Invalid package/cna name.");
        }

        std::string packageNameWithSeparator;
        packageNameWithSeparator.append(package.name);
        packageNameWithSeparator.append("_CVE");

        for (const auto& [key, value] : m_feedDatabase->seek(packageNameWithSeparator, cnaName))
        {
            if (flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(value.data()), value.size());
                !NSVulnerabilityScanner::VerifyScanVulnerabilityCandidateArrayBuffer(verifier))
            {
                throw std::runtime_error(
                    "Error getting ScanVulnerabilityCandidateArray object from rocksdb. FlatBuffers verifier failed");
            }

            auto candidatesArray = GetScanVulnerabilityCandidateArray(reinterpret_cast<const uint8_t*>(value.data()));

            if (candidatesArray)
            {
                for (const auto& candidate : *candidatesArray->candidates())
                {
                    if (callback(cnaName, package, *candidate))
                    {
                        // If the candidate is vulnerable, we stop looking for.
                        break;
                    }
                }
            }
        }
    }

    /**
     * @brief Retrieves a reference to the CVE (Common Vulnerabilities and Exposures) database.
     *
     * This function provides access to the Common Vulnerabilities and Exposures (CVE) database
     * represented by a reference to a RocksDBWrapper object.
     *
     * @return A reference to the CVE database represented by TRocksDBWrapper.
     */
    TRocksDBWrapper& getCVEDatabase()
    {
        return *m_feedDatabase;
    }

    /**
     * @brief Updates scheduler interval.
     *
     * @param data Data containing the interval.
     */
    void update(nlohmann::json& data) override
    {
        if (m_contentRegistration)
        {
            m_contentRegistration->changeSchedulerInterval(data.at("updater").at("interval").get<size_t>());
        }
    }
    // LCOV_EXCL_STOP

    /**
     * @brief Gets descriptive information for a given CVE ID and CNA/ADP.
     *
     * @param cveId CVE ID to get the information.
     * @param subShortName Expanded CNA/ADP name (Ex. nvd, suse_server_15, redhat_8)
     * @param resultContainer container struct to store the result.
     *
     * @return true if the information was successfully retrieved, false otherwise.
     */
    bool getVulnerabilityDescriptiveInformation(const std::string& cveId,
                                                const std::string& subShortName,
                                                FlatbufferDataPair<VulnerabilityDescription>& resultContainer)
    {
        if (const auto descriptionColumn = std::string(DESCRIPTIONS_COLUMN) + "_" + subShortName;
            m_feedDatabase->get(cveId, resultContainer.slice, descriptionColumn) == false)
        {
            logDebug2(WM_VULNSCAN_LOGTAG,
                      "Vulnerability description not found for %s in %s.",
                      cveId.c_str(),
                      descriptionColumn.c_str());
            resultContainer.data = nullptr;
            return false;
        }

        resultContainer.data = const_cast<NSVulnerabilityScanner::VulnerabilityDescription*>(
            NSVulnerabilityScanner::GetVulnerabilityDescription(resultContainer.slice.data()));

        return true;
    }

    /**
     * @brief Get CNA/ADP name based on the package source.
     *
     * @param source Package source.
     * @return std::string CNA/ADP name. Empty string otherwise.
     */
    std::string getCnaNameBySource(std::string_view source) const
    {
        if (const auto& vendorMap = GlobalData::instance().vendorMaps(); vendorMap.contains("source"))
        {
            for (const auto& item : vendorMap.at("source"))
            {
                if (source == item.begin().key())
                {
                    return item.begin().value();
                }
            }
        }

        return {};
    }

    /**
     * @brief Get CNA/ADP name based on the package format.
     * @param format Package format.
     * @return CNA/ADP name. Empty string otherwise.
     */
    std::string getCnaNameByFormat(std::string_view format) const
    {
        if (const auto& vendorMap = GlobalData::instance().vendorMaps(); vendorMap.contains("format"))
        {
            for (const auto& item : vendorMap.at("format"))
            {
                if (format == item.begin().key())
                {
                    return item.begin().value();
                }
            }
        }

        return {};
    }

    /**
     * @brief Get CNA/ADP name based on the package vendor when it contains a specific word.
     * @param vendor Package vendor.
     * @param platform Os platform.
     * @return CNA/ADP name. Empty string otherwise.
     */
    std::string getCnaNameByContains(std::string_view vendor, std::string_view platform) const
    {
        if (const auto& vendorMap = GlobalData::instance().vendorMaps(); vendorMap.contains("contains"))
        {
            for (const auto& item : vendorMap.at("contains"))
            {
                if (const auto& platforms = item.begin().value().at("platforms");
                    vendor.find(item.begin().key()) != std::string::npos &&
                    std::find(platforms.begin(), platforms.end(), platform) != platforms.end())
                {
                    return item.begin().value().at("cna");
                }
            }
        }

        return {};
    }

    /**
     * @brief Get CNA/ADP name based on the package vendor when it starts with a specific word.
     * @param vendor Package vendor.
     * @param platform Os platform.
     *
     * @return CNA/ADP name. Empty string otherwise.
     */
    std::string getCnaNameByPrefix(std::string_view vendor, std::string_view platform) const
    {
        if (const auto& vendorMap = GlobalData::instance().vendorMaps(); vendorMap.contains("prefix"))
        {
            for (const auto& item : vendorMap.at("prefix"))
            {
                if (const auto& platforms = item.begin().value().at("platforms");
                    Utils::startsWith(vendor.data(), item.begin().key()) &&
                    std::find(platforms.begin(), platforms.end(), platform) != platforms.end())
                {
                    return item.begin().value().at("cna");
                }
            }
        }

        return {};
    }

    /**
     * @brief Get cache size from configuration.
     *
     * This function retrieves the cache size for translation cache from the configuration settings.
     * It accesses the instance of TPolicyManager to fetch the translation LRU size specified in the configuration.
     *
     * @return The size of the translation cache as specified in the configuration settings.
     */
    uint32_t getCacheSizeFromConfig() const
    {
        return TPolicyManager::instance().getTranslationLRUSize();
    }

private:
    /**
     * Do not change the order of definition of these variables.
     * Since it is important at the object destruction time.
     */
    std::shared_mutex& m_mutex;
    std::unique_ptr<TContentRegister> m_contentRegistration;
    std::shared_ptr<TRocksDBWrapper> m_feedDatabase;
    std::unique_ptr<TranslationLRUCache> m_translationL2Cache =
        std::make_unique<TranslationLRUCache>(TPolicyManager::instance().getTranslationLRUSize());

    /**
     * @brief Reads the vendor and os cpe maps from the database and loads the data into memory.
     *
     * @throws std::runtime_error if the vendor and os cpe maps aren't available or are invalid.
     * @note This methods locks the mutex.
     */
    void reloadGlobalMaps()
    {
        std::scoped_lock<std::shared_mutex> lock(m_mutex);

        std::string result;
        if (!m_feedDatabase->get("FEED-GLOBAL", result, VENDOR_MAP_COLUMN))
        {
            throw std::runtime_error("Vendor map can not be found in DB.");
        }
        else if (result.empty())
        {
            throw std::runtime_error("Vendor map is empty.");
        }

        GlobalData::instance().vendorMaps(nlohmann::json::parse(result));

        rocksdb::PinnableSlice queryResult;
        if (!m_feedDatabase->get("OSCPE-GLOBAL", queryResult, OS_CPE_RULES_COLUMN))
        {
            throw std::runtime_error("Error getting OS CPE rules content from rocksdb.");
        }
        GlobalData::instance().osCpeMaps(nlohmann::json::parse(queryResult.ToString()));

        // Load CNA mappings
        if (!m_feedDatabase->get("CNA-MAPPING-GLOBAL", queryResult, CNA_MAPPING_COLUMN))
        {
            throw std::runtime_error("Error getting CNA Mapping content from rocksdb.");
        }
        GlobalData::instance().cnaMappings(nlohmann::json::parse(queryResult.ToString()));

        // Load translations into the Level 2 cache
        fillL2CacheTranslations();
    }
};

using DatabaseFeedManager = TDatabaseFeedManager<>;

#endif // _DATABASE_FEED_MANAGER_HPP
